package thread.wang_bao_ling.c15;

import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 通过ReentrantLock实现简单的阻塞队列
 * 对比Synchronized通过wait，notifyAll实现阻塞队列，使用ReentrantLock和Condition实现的管程支持多条件变量，而Synchronized实现的管程只支持一个条件变量
 * 比如使用Synchronizatized实现阻塞队列的时候入队和出队的wait都会存在同一个条件等待队列中，因为Synchronizatized实现的管程只支持一个条件变量，执行notify或notifyAll时不能精确通知入队或出队的wait
 * 而使用Conditiond时定义的notFull和notEmpty是两个条件变量，会有两个等待队列，调用对应条件变量会进入对应的等待队列中
 *
 * @1和@2处await的线程分别会进入notFull条件变量的等待队列和notEmpty条件变量的等待队列
 */
public class ReentrantLockQueue {
    final ReentrantLock lock = new ReentrantLock();
    // 条件变量：队列不能满
    final Condition notFull = lock.newCondition();
    // 条件变量：队列不能为空
    final Condition notEmpty = lock.newCondition();

    private List queue = new LinkedList();

    private int limit = 2;

    // 入队
    public void enq(Object x) throws InterruptedException {
        lock.lock();
        try {
            while (this.queue.size() == this.limit) {
                // 满足队列不能满条件，需要wait
                notFull.await();//@1
            }
            notEmpty.signal();
            System.out.println(" notEmpty.signal(); end");
            this.queue.add(x);//notEmpty.signal只会让条件队列中等待的线程进入锁等待队列，此时锁还未释放，所以将this.queue.add放在notEmpty.signal后是可以的
        } finally {
            lock.unlock();
        }
    }

    // 出队
    public Object deq() throws InterruptedException {
        lock.lock();
        try {
            while (this.queue.size() == 0) {
                // 满足队列不能为空条件，需要wait
                notEmpty.await();//@2
            }
            notFull.signal();
            System.out.println(" notEmpty.signal(); end");
            return this.queue.remove(0);
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        ReentrantLockQueue queue = new ReentrantLockQueue();
        deq(queue, "t1",0);
        deq(queue, "t2",0);
        enq(queue, "one", "t3",5000);
        enq(queue, "two", "t4",10000);
    }

    public static void enq(ReentrantLockQueue queue, Object o, String threadName,int delay) {
        new Thread(() -> {
            try {
                if (delay > 0) {
                    Thread.currentThread().sleep(delay);
                }
                queue.enq(o);
                System.out.println("enq--->"+o);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, threadName).start();
    }

    public static void deq(ReentrantLockQueue queue, String threadName,int delay) {
        new Thread(() -> {
            try {
                if (delay > 0) {
                    Thread.currentThread().sleep(delay);
                }
               Object o= queue.deq();
                System.out.println("deq--->"+o);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, threadName).start();
    }
}
